home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
assemblr
/
library
/
sampler0
/
ted2.asm
< prev
next >
Wrap
Assembly Source File
|
1989-09-12
|
79KB
|
2,365 lines
NAME TED
PAGE 58,132
TITLE TED -- the Tiny EDitor
;=======================================================================
; TED.ASM -- The Tiny EDitor.
; PC Magazine * Tom Kihlken * 11/15/88
;
; See the article "The tiniest editor you'll ever need" by Tom Kihlken,
; in the November 15, 1988 issue of PC Magazine for more detail than is
; presented here. Portions of this text are excerpted from that article.
; The source listing, itself, was copied directly from that article and
; altered as appropriate to add comments or change or improve the program.
; The use of self-modifying code for "NO_DESNOW" is discussed in the article.
; SEGment register usage have been cleaned up to reduce the size of the code.
;
; TED follows normal text conventions: pressing the <Enter> key actually
; adds two characters, Carriage-return (ASCII 13) and Line-feed (ASCII 10),
; also known as a "hard carriage return". The <Tab> key inserts the ASCII 9
; tab character, and advances the screen cursor to the next column that is
; an even multiple of eight. The <Backspace> key deletes the character to
; its immediate left ("destructive" backspace) and combines two lines if it
; is pressed while in the first column. The Backspace code (Ctrl-H) may be
; entered in the file as a control code. TED2 allows the ASCII codes for
; Carriage-Return (Ctrl-M) and Line-feed (Ctrl-J) to independently be entered
; into the file as control codes. Because of this, TED2 rigidly enforces
; a convention that once the <Ctrl-M><Ctrl-J> pair is formed it becomes an
; End-of-line marker, and may not be separated again.
;
; Like DOS, TED lets you enter any character (except <Nul>, value 00) by
; holding down the Alt key, typing the decimal value of its ASCII code on
; the numeric keypad, then releasing Alt. This gives you access both to
; nonprinting codes below ASCII 32 and to the upper-order (ASCII 128 to 255)
; characters in the extended IBM set. TED2 allows the <Nul> code to be
; entered by pressing the <Shft-F1> key.
;
; NOTE: Unassigned keyboard codes are treated as no-operations.
;
; The TED Keypad commands are:
; Key Description
; Up Arrow Moves cursor up one row
; Down Arrow Moves cursor down one row
; Left Arrow Moves cursor left one column
; Right Arrow Moves cursor right one column
; PgUp Moves text window up one page
; PgDn Moves text window down one page
;>Home Moves cursor to start of row (if at home, next up)
;>End Moves cursor to end of row (if at end, next end down)
; Ins Toggles insert/overstrike mode
; Del Deletes character (right) at cursor (saved in UnDo)
; Backspace Deletes character (left) at cursor (not saved in UnDo)
; Ctrl-PgUp Moves to top of file
;>Ctrl-Home Moves to top of file (alias)
; Ctrl-PgDn Moves to bottom of file
;>Ctrl-End Moves to bottom of file (alias)
; Ctrl-Right Arrow Moves text window right eight columns
; Ctrl-Left Arrow Moves text window left eight columns
;
; Alt-S Search Search for (case insensitive) character string
; Alt-A Search Again Search again for string
; Alt-D Del EOL Deletes from cursor to the end of line
;
; The TED Editing Functions are:
; Key Function Operation
; F1 Help Help screen
; ShftF1 <Nul> Enters the <Nul> code (value 00) in the file
; F2 Exit Save changes and exit (creates a .BAK file)
; ShftF2 Quit Exit without saving changes
; F3 Print Prints the marked text
; F4 Mark Toggles mark state on/off
; F5 Cut Moves marked text to paste buffer
; ShftF5 Paste Inserts contents of paste buffer (64K char's max)
; F6 Search Search for (case insensitive) character string
; ShftF6 Search Again Search again for string
; F7 UnDo Replaces recently DELeted characters (256 char's max)
; F8 Del EOL Deletes from cursor to the end of line
; ShftF8 Del L Deletes the current line (MultiEdit)
; F9 Del L Deletes the current line
; F10 Udel L Inserts the last deleted line (256 char's max)
;
; In addition to the 64K Text buffer (where the edit file is maintained),
; there are three other important buffers, as indicated above.
; 1.) The paste buffer is a 64K character buffer used for MARK/CUT/PASTE of
; sections of the file.
; 2.) The line buffer is a 256 character buffer used to save the last
; deleted line of text (used with F8, F9 and F10).
; 3.) The UnDo buffer is a 256 character buffer used to save the just
; deleted characters at the current cursor location. It works with
; the "DEL" key, but only the last character with the "Backspace" key.
; It also saves overwritten characters in the Overstrike mode.
; Deletions are restored with "F7" UnDo.
;
; NOTE: There is a severe subroutine nesting violation in the TED key scan
; routine. BAD_KEY from within the keyboard scanning loop is a junp to
; read next key. The BAD_KEY subroutine calls through the DISPATCH_TABLE
; do not have a corresponding return to equalize the stack. This was
; partially compensated for by making the stack humongous. TED 1.0 as
; published in PC Magazine could eventually overflow the stack and crash the
; system. TED2 has corrected this problem by making a stub subroutine for
; the DISPATCH_TABLE BAD_KEY subroutine CALLs.
;
; TED was modified by James E. Galbraith, Dec 18, 1988.
; The changes are of several types. Compatability with other editors.
; Correction of errors and questionable code. Reordering of program modules
; to make it easier to follow the listing. Addition of meaningful comments.
; These modifications include:
; Change F2 "UnDo" to "Exit" (for BSE compatibility).
; Change F7 "Exit" to "UnDo".
; Make CTRL-HOME alias to CTRL-PGUP, to home the cursor (for BSE).
; Make CTRL-END alias to CTRL-PGDN, cursor to end of file (for BSE).
; JMP instructions outside modules instead of CALL/RETurn are commentd.
; Assignment of DS was sometimes ambiguous.
;
; Modified by James E. Galbraith, June 1989.
; The changes are in two groups. Those taken from TEDPLUS (apparently also
; written by Tom Kihlken), which was acquired from a bulletin board service,
; and my own changes.
; Add SEARCH and SEARCH AGAIN as F6 and Shft-F6, from TEDPLUS.
; Delete CPM style Ctrl-Z End-of-file code, from TEDPLUS.
; "Enter any key code" from TEDPLUS was included, but not like TEDPLUS
; End-of-line was changed to CR-LF as in TEDPLUS. CR or LF codes may be
; entered separately.
; My own changes are:
; Close the source file if it has been opened. This was not done by TED.
; Add a HELP screen as <F1>, move QUIT to <Shft-F2>.
; Fix the SEARCH inverse video to turn off when any key is pressed.
; Add DOS version check, needs version 2.0 or higher to run.
; Add a program checksum test for verification of file integrity.
; Add <Alt-S> as alias for <F6> SEARCH (usage in BSE editor).
; Add <Alt-A> as alias for <Shft-F6> SEARCH AGAIN (usage in BSE editor).
; Add <Alt-D> as alias for <Shft-F8>, Delete to end of line.
; Add <Shft-F8> as alias for <F9>, Delete Line (for future expansion).
; If the file has not been altered, don't prompt for save or quit.
; On QUIT, accept a CR as confirmation of intention to abandon file.
; Add <Shft-F1> as key to enter the <Nul> character, 00.
; If a CR and LF code are made to be ajacent, they become an EOL marker,
; and may not then be separated. This can occur in several ways that
; all include entering the codes <Crtl-M> and <Ctrl-J>.
; A key entry will interrupt a screen update that is in progress.
;
; Assemble the TED2.ASM source file to TED2.COM by using the MASM.EXE assembler,
; the linker LINK.EXE, and the file conversion utility EXE2BIN.EXE, as follows:
; The checksum byte can be obtained with TEST.BAT (included with TED2.ASM).
;
; MASM TED2;
; LINK TED2;
; EXE2BIN TED2 TED2.COM
;-----------------------------------------------------------------------
CSEG SEGMENT
ASSUME CS:CSEG, DS:CSEG, ES:CSEG
ORG 0100H
START:
JMP BEGIN
;-----------------------------------------------------------------------
; Local data area in CSEG.
;-----------------------------------------------------------------------
; ASCII character values used in the program.
TAB EQU 9
CR EQU 13
LF EQU 10
CRLF EQU 0A0DH
; Character strings and data
COPYRIGHT DB "TED 1.0 (C) 1988 Ziff Communications Co.,"
DB "PC Magazine, 11/15/88, by Tom Kihlken"
DB CR,LF,"TED 2.1, 9/12/89, by James E. Galbraith"
DB CR,LF,"$"
;(JEG)
HELP_SCREEN DB 9,9,'TED - the Tiny EDitor (any key to edit)',CR
DB LF,LF,9,'F1 - Help',9,9,9,'Shft-F1 - <NUL> char'
DB CR,LF,9,'F2 - Exit and save',9,9,'Shft-F2 - Quit'
DB CR,LF,9,'F3 - Print marked',9,9,'Shft-F3 - Form-feed'
DB CR,LF,9,'F4 - Mark toggle'
DB CR,LF,9,'F5 - Cut marked',9,9,9,'Shft-F5 - Paste'
DB CR,LF,9,'F6 - String search',9,9,'Shft-F6 - '
DB 'Search again'
DB CR,LF,9,'F7 - Undo <Del> or Ovr'
DB CR,LF,9,'F8 - Delete to EOL' ;** ,9,9,'Shft-F8 - '
;** DB 'Delete Line'
DB CR,LF,9,'F9 - Delete Line'
DB CR,LF,9,'F10 - Un-Delete line',LF
DB CR,LF,9,'Ctrl-L/R arrow - Move window'
DB CR,LF,9,'Ctrl-Home - to top'
DB CR,LF,9,'Ctrl-End - to EOF',LF
DB CR,LF,9,'Esc - From Exit'
DB CR,LF,'$',7,1AH
;from JEG and TEDPLUS
PROMPT_STRING DB "1Help",0 ;F1
DB "2Exit",0 ;F2
DB "3Print",0 ;F3
DB "4Mark",0 ;F4
DB "5Cut",0 ;F5
DB "6Search",0 ;F6
DB "7UnDo",0 ;F7
DB "8Del EOL",0 ;F8
DB "9Del L",0 ;F9
DB "10Udel L",0,0 ;F10
PROMPT_LENGTH = $ - OFFSET PROMPT_STRING
FILE_TOO_BIG DB "File too big$"
READ_ERR_MESS DB "Read error$"
MEMORY_ERROR DB "Not enough memory$"
;(JEG)
CHEK_SUM_MESS DB "TED altered$"
DOS_2_MESS DB "Needs DOS 2.0$"
VERIFY_MESS DB "Lose Changes (Y)?",0
SAVE_MESS DB "Save as: ",0
DOT_$$$ DB ".$$$",0
DOT_BAK DB ".BAK",0
;from TEDPLUS, string search
SRCH_PROMPT DB "SEARCH> ",0
;SRCH_MAX DB 66 ;(moved to file end to reduce file size)
;SRCH_SIZ DB 0
;SRCH_STR DB 66 DUP (0)
SRCH_FLG DB 0 ;0=normal, 1=search successful (inverse)
SRCH_END DW 0
SRCH_BASE DW 0
SRCH_CLR DB 0F0H ;244
DIRTY_BITS DB 03H ;1=update screen, 2=cursor 4=cur. line
DIRTY_FILE DB 0 ;0=file not altered, FF=file altered
NORMAL DB 07H ;video attribute bits
INVERSE DB 70H ;video (inverse)
LEFT_MARGIN DB 0 ;column number of screen left margin
MARGIN_COUNT DB 0
INSERT_MODE DB 01H ;1=INSert, 0=Overstrike, 2or3=Read Only
INSERT_CODE DB 'OIRR' ;indexed code for display screen
MARK_MODE DB 0 ;toggle, FF=MARK is on.
ROWS DB 23 ;Rows available on display screen
SAVE_COLUMN DB 0
SAVE_ROW DB 0
LINE_FLAG DB 0
EVEN ;"word align" (for 16-bit bus accesses)
NAME_POINTER DW 81H ;offset of command tail in PSP
NAME_END DW 81H ;end of tail
VIDEO_STATUS_REG LABEL DWORD ;(alias for use by LES instruction)
STATUS_REG DW 0 ;video status register offset
VIDEO_SEG DW 0B000H ;video segment, Mono=0B000, other 0B800
LINE_LENGTH DW 0 ;number of bytes in current line
UNDO_LENGTH DW 0 ;number of DELeted bytes in UnDo buffer
CURS_POSN DW 0 ;Cursor, Hi is row, Low is column
MARK_START DW 0FFFFH ;MARK text for CUT/PASTE or Print
MARK_END DW 0
MARK_HOME DW 0
TOP_OF_SCREEN DW 0 ;address in text file of Top-of-screen
CURSOR DW 0 ;address in text file of cursor
LAST_CHAR DW 0 ;address of last character in file.
COLUMNSB LABEL BYTE ;alias BYTE definition
COLUMNS DW 0
PASTE_SEG DW 0 ;segment address of PASTE buffer
PASTE_SIZE DW 0 ;size of block in PASTE buffer
PAGE_PROC DW 0 ;pointer used by PGUP and PGDN
EXIT_CODE DW 4C00H ;DOS INT 21H, Func 4CH, exit with code
OLDINT24 DD 0 ;pointer to DOS critical error handler
DISPATCH_BASE EQU 59 ;initial offset for a PASCAL type CASE list
DISPATCH_TABLE DW HELP ;59;F1, Help screen (common usage)
DW EXIT ;60;F2, Save changes and exit (from BSE)
DW PRINT ;61;F3, Print the marked text
DW MARK ;62;F4, Toggle mark state on/off
DW CUT ;63;F5, Move marked text to buffer
DW FIND_STR ;64;F6, Search for text string
DW UNDO ;65;F7, Replace recently deleted chars
DW DEL_EOL ;66;F8, Delete from cursor to EOL
DW DEL_L ;67;F9, Delete the current line
DW UDEL_L ;68;F10, Insert the last deleted line
DW BAD_KEY ;69;(NumLock)
DW BAD_KEY ;70;(ScrollLock)
DW HOME_KEY ;71;Home/7
DW UP ;72;Up/8
DW PGUP ;73;PgUp/9
DW BAD_KEY ;74;(-)
DW LEFT ;75;Left/4
DW BAD_KEY ;76;(5)
DW RIGHT ;77;Right/6
DW BAD_KEY ;78;(+)
DW END_KEY ;79;End/1
DW DOWN ;80;Down/2
DW PGDN ;81;PgDn/3
DW INSERT ;82;Ins/3
DW DEL_CHAR ;83;Del/.
DW NUL_CHAR ;84;Shft-F1 -- Add NUL character to file
DW ABORT ;85;Shft-F2 -- Quit and abandon changes
DW PRINT_FF ;86;Shft-F3 -- Print a form-feed character
DW BAD_KEY ;87;Shft-F4
DW PASTE ;88;Shft-F5 -- Insert contents of Paste buffer
DW FIND_STR ;89;Shft-F6 -- Search again for string
DW BAD_KEY ;90;Shft-F7
DW DEL_L ;91;Shft-F8 -- Delete line (Multi-Edit)
DISPATCH_END EQU 92
;--------------------------------
; gap in keyboard scan code assignments
;--------------------------------
DISP_CURS_BASE EQU 115
DW SH_LEFT ;115;Ctrl-Left arrow
DW SH_RIGHT ;116;Ctrl-Right arrow
DW BOTTOM ;117;Ctrl-End
DW CTRL_PGDN ;118;Ctrl-PgDn
DW TOP ;119;Ctrl-Home
DISP_CURS_END EQU 120
;** DW CTRL_PGUP ;132;Ctrl-PgUp
;---------------------------------
; The following constant is a machine instruction that removes the CGA desnow
; delay. It is inserted into the code for EGA, VGA, and mono displays.
; See the article in PC-Magazine for further discussion.
NO_DESNOW = (OFFSET WRITE_IT - OFFSET HWAIT - 2) * 256 + 0EBH ;opcode JMP SHORT
;=======================================================================
; We start by initialize the display, then allocate memory for the file
; and paste segments. Parse the command line for the filename, if one was
; input, read in the file. Finally set the INT 23 and 24 vectors.
;-----------------------------------------------------------------------
BEGIN:
MOV AH,30H ;get DOS version (JEG new)
INT 21H
CMP AL,2
JAE DOS_2_UP
MOV EXIT_CODE,0 ;is DOS version 1, exit with function 0.
MOV DX,OFFSET DOS_2_MESS ;message "Needs DOS 2.0"
;**short JMP EXIT_TO_DOS
JMP SHORT EXIT_22_DOS ;(saves a byte)
;-----------------------
DOS_2_UP:
;JEG new -- program code checksum test
MOV SI,100H ;start of program file.
MOV SP,SI ;set stack pointer to top of PSP.
MOV CX,OFFSET CHEK_SUM_BYT - 0100H ;size of program file.
MOV AL,CHEK_SUM_BYT ;load checksum correction factor byte.
CHKSUM_LOOP: ADD AL,[SI] ;add all program code bytes together.
INC SI
LOOP CHKSUM_LOOP
CMP AL,0 ;is program checksum zero?
JZ CHEKSUM_IS_OK ;yes, continue with program
NEG AL ;make error into correction factor.
MOV BYTE PTR EXIT_CODE,AL ;save errorlevel code for exit.
MOV DX,OFFSET CHEK_SUM_MESS ;message "TED altered".
;**short JMP EXIT_TO_DOS ;error message and exit to DOS
;EXIT_22_DOS: JMP SHORT EXIT_2_DOS ;error message and exit to DOS
EXIT_22_DOS: JMP EXIT_2_DOS ;error message and exit to DOS
;-----------------------
CHEKSUM_IS_OK:
XOR AX,AX
MOV DS,AX ;zero DS
ASSUME DS:NOTHING
MOV BL,10H
MOV AH,12H ;get EGA info
INT 10H
CMP BL,10H ;did BL change?
JE NOT_EGA ;if not, no EGA in system
TEST BYTE PTR DS:[0487H],8 ;is EGA active?
JNZ NOT_EGA
MOV WORD PTR CS:HWAIT,NO_DESNOW ;get rid of desnow
MOV AX,DS:[0484H] ;get number of rows
DEC AL ;last row is for prompt line
MOV CS:[ROWS],AL ;save the number of rows
NOT_EGA:
MOV AX,DS:[044AH] ;get number of columns
MOV CS:COLUMNS,AX ;and store it
MOV AX,DS:[0463H] ;address of display card
ADD AX,6 ;add six to get status port
PUSH CS
POP DS
ASSUME DS:CSEG
MOV STATUS_REG,AX
CMP AX,3BAH ;is this a MONO display?
JNE COLOR ;if not, must be a CGA
MOV WORD PTR HWAIT,NO_DESNOW ;get rid of desnow
JMP SHORT MOVE_STACK
COLOR:
MOV VIDEO_SEG,0B800H ;segment for color card
XOR BH,BH ;use page zero
MOV AH,8 ;get current attribute
INT 10H
AND AH,77H ;turn off blink and high intensity
MOV NORMAL,AH ;save the normal attribute
XOR AH,01110111B ;flip the color bits
MOV INVERSE,AH
OR AH,80H
MOV SRCH_CLR,AH ;search is inverse/blink
MOVE_STACK:
MOV BX,OFFSET END_BUFFER
;**rounding ADD BX,15 ;add offset value for SEG rounding up
MOV CL,4 ;convert program size to
SHR BX,CL ; paragraphs
MOV AH,4AH ;deallocate unused memory
INT 21H ;DOS call
MOV BX,1000H ;request 64K for file segment
MOV AH,48H
INT 21H ;DOS call
MOV ES,AX
ASSUME ES:FILE_SEG
MOV AH,48H ;request 64K for paste buffer
INT 21H ;DOS call
JNC GOT_ENOUGH ;if enough memory, continue
NOT_ENOUGH:
MOV DX,OFFSET MEMORY_ERROR
EXIT_2_DOS: JMP EXIT_TO_DOS ;jump island to allow SHORT jumps.
;-----------------------
GOT_ENOUGH:
MOV PASTE_SEG,AX ;use this for the paste buffer
GET_FILENAME:
MOV SI,80H ;point to command tail in PSP
MOV CL,[SI] ;get number of characters in tail
XOR CH,CH ;make it a word
INC SI ;point to first character
PUSH SI
ADD SI,CX ;point to last character
MOV BYTE PTR [SI],0 ;make it an ASCIIZ string (clear the CR)
MOV NAME_END,SI ;save pointer to last character
POP SI ;get back pointer to filename
JCXZ NO_FILENAME ;if no params, just exit
CLD
DEL_SPACES:
LODSB ;get character into AL
CMP AL," " ;is it a space?
JNE FOUND_LETTER
LOOP DEL_SPACES
FOUND_LETTER:
DEC SI ;backup pointer to first letter
MOV NAME_POINTER,SI ;save pointer to filename
MOV DX,NAME_POINTER
MOV AX,4300H ;get file attribute byte (in CL)
INT 21H ;DOS call
JC ATTRIB_ERROR
AND CL,1 ;keep bit-0, read-only status
ADD CL,CL ;shift RO bit up
OR INSERT_MODE,CL ;save as index modifier value
ATTRIB_ERROR:
MOV DX,SI
MOV AX,3D00H ;setup to open file
INT 21H ;DOS call
; If no carry, Then the opened file handle is in AX
; If no carry, Then the opened file handle is in AX
JC NO_FILENAME ;if we can't open, must be new file
FILE_OPENED:
PUSH ES
POP DS ;DS has file segment also
ASSUME DS:FILE_SEG
MOV BX,AX ;get the handle into BX
XOR DX,DX ;point to file buffer, DS:DX
MOV CX,0FFFEH ;read almost 64K bytes
MOV AH,3FH ;read from file or device
; BX = file handle
INT 21H ;DOS call
; If no carry, Then AX contains number of bytes read
MOV DI,AX ;number of bytes read in
JNC FILE_READ_OK ;if no error, take jump
MOV DX,OFFSET READ_ERR_MESS
;-----------------------
; the file has been opened, it should now be closed. (JEG)
;-----------------------
CLOSE_ERR_EXIT:
MOV AH,3EH ;close the opened file
; BX = file handle
INT 21H
JMP SHORT EXIT_2_DOS
;-----------------------
FILE_READ_OK:
MOV LAST_CHAR,DI ;save the file size
CMP CX,AX ;did the buffer fill?
MOV DX,OFFSET FILE_TOO_BIG
JE CLOSE_ERR_EXIT ;if yes, it is too big
MOV AH,3EH ;close the file
; BX = file handle
INT 21H
;**dirty MOV BYTE PTR CS:DIRTY_FILE,0 ;file opened but not altered.
NO_FILENAME:
PUSH ES
PUSH ES ;save the file segment
PUSH CS
POP DS
ASSUME DS:CSEG
;-----------------------
;INT 24 is the critical error handler
;-----------------------
MOV AX,3524H ;get INT 24 vector
INT 21H ;DOS call
MOV WORD PTR OLDINT24,BX ;store the offset
MOV WORD PTR OLDINT24+2,ES ;and the segment
MOV DX,OFFSET NEWINT24 ;point to new vector
MOV AX,2524H ;now change INT 24 vector
INT 21H ;DOS call
;-----------------------
;INT 23 is the CTRL-Break or CTRL-C handler
;-----------------------
MOV DX,OFFSET NEWINT23
MOV AX,2523H ;set the INT 23 vector
INT 21H ;DOS call
POP ES ;get back file segment
POP DS
ASSUME DS:FILE_SEG
CALL REDO_PROMPT ;draw the prompt line
;-----------------------------------------------------------------------
; Here's the main loop. It updates the screen, then reads a keystroke.
;-----------------------------------------------------------------------
READ_A_KEY:
CMP MARK_MODE,0 ;is the mark state on?
JZ MARK_OFF ;if not, skip this
OR DIRTY_BITS,4 ;refresh the current row
MOV DX,CURS_POSN
CMP SAVE_ROW,DH ;are we on the save row?
JE SAME_ROW ;if yes, then redo the row only
OR DIRTY_BITS,1 ;refresh the whole screen
SAME_ROW:
MOV AX,CURSOR ;get cursor location
MOV BX,MARK_HOME ;get the anchor mark position
CMP AX,BX ;moving backward in file?
JAE SAME1
MOV MARK_START,AX ;switch start and end positions
MOV MARK_END,BX
JMP SHORT MARK_OFF
SAME1:
MOV MARK_END,AX ;store start and end marks
MOV MARK_START,BX
MARK_OFF:
CALL DISPLAY_SCREEN ;redraw the screen
XOR AH,AH ;read the next key (wait if none ready)
INT 16H ;BIOS call
; AL = ASCII code or 0 (for function keys)
; AH = scan code
TEST SRCH_FLG,0FFH
JZ CHECK_KEY ;jump if inverse not on
MOV SRCH_FLG,0 ;turn off highlight
OR DIRTY_BITS,1 ;redraw screen (next time)
CHECK_KEY:
OR AL,AL ;is this an extended code?
JZ IS_EXTENDED_CODE ;(jump if not an ASCII character)
CALL INSERT_KEY ;put this ASCII character in the file
;**short JMP READ_A_KEY ;get another key
JMP SHORT RD_NEXT_KEY ;get another key
;---------------------------------------
IS_EXTENDED_CODE:
;-----------------------
; The following code is for "orphan" key codes and alias keys
;-----------------------
CMP AH,132 ;is it Ctrl-PgUp? (an orphan code)
JNE NOT_CT_PGUP
CALL CTRL_PGUP
;**short JMP READ_A_KEY
JMP SHORT RD_NEXT_KEY ;get another key
;-----------------------
NOT_CT_PGUP:
CMP AH,32 ;is it Alt-D (MultiEdit Del-EOL)?
JNE NOT_ALT_D
MOV AH,66 ;substitute scan code for F8
NOT_ALT_D:
CMP AH,31 ;is it Alt-S (BSE string search)?
JNE NOT_ALT_S
MOV AH,64 ;substitute scan code for F6
NOT_ALT_S:
CMP AH,30 ;is it Alt-A (BSE search again)?
JNE NOT_ALT_A
MOV AH,89 ;substitute scan code for Shft-F6
NOT_ALT_A:
;-----------------------
; The following code sets up a PASCAL style CASE statement.
;-----------------------
CMP AH,DISPATCH_END ;split the dispatch table
JB DO_DISPATCH
;-----------------------
; offset cursor group of keys to join the regular dispatch table.
;-----------------------
CMP AH,DISP_CURS_BASE
JB RD_NEXT_KEY
CMP AH,DISP_CURS_END
JAE RD_NEXT_KEY
SUB AH,LOW (DISP_CURS_BASE - DISPATCH_END) ;close table gap
DO_DISPATCH:
;-----------------------
; This is a PASCAL style CASE statement.
;-----------------------
MOV BX,AX ;put AH offset value in BX, AL=0
XCHG BL,BH ;make into a proper word
SUB BX,DISPATCH_BASE ;zero offset for dispatch table jump
JC RD_NEXT_KEY ;too low, not in table
ADD BX,BX ;make into word
CALL CS:DISPATCH_TABLE[BX] ;call the key procedure
RD_NEXT_KEY: JMP READ_A_KEY ;then read another key
;=======================================================================
; KEYBOARD and CURSOR services
;=======================================================================
; These two routines shift the display right or left to allow editing
; files which contain lines longer than 80 columns. Starting with TED2,
; they are proper subroutines.
;-----------------------------------------------------------------------
SH_LEFT PROC NEAR
CMP LEFT_MARGIN,0 ;at start of line already?
JE NO_SHIFT ;if yes,then don't shift
SUB LEFT_MARGIN,8 ;move the window over
JMP SHORT SH_RETURN
SH_LEFT ENDP
;-----------------------------------------------------------------------
SH_RIGHT PROC NEAR
CMP LEFT_MARGIN,255 - 8 ;past max allowable margin?
JAE NO_SHIFT ;then can't move any more
ADD LEFT_MARGIN,8 ;this moves the margin over
SH_RETURN:
CALL CURSOR_COL ;compute column for cursor
MOV SAVE_COLUMN,DL ;save the current column
OR DIRTY_BITS,1 ;redraw the screen
NO_SHIFT:
;**dispatch JMP READ_A_KEY
;**bad_key RET
SH_RIGHT ENDP
;-----------------------------------------------------------------------
; DISPATCH_TABLE calls to BAD_KEY now go here so that the stack can be
; kept equallized.
;-----------------------------------------------------------------------
BAD_KEY PROC NEAR
RET
BAD_KEY ENDP
;=======================================================================
; This routine moves the cursor to the top of the file.
;-----------------------------------------------------------------------
TOP PROC NEAR
XOR AX,AX ;get a zero into AX
MOV CURSOR,AX ;cursor to start of file
MOV TOP_OF_SCREEN,AX
MOV LEFT_MARGIN,AL ;move to far left margin
MOV CURS_POSN,AX ;home the cursor
MOV SAVE_COLUMN,AL ;save the cursor column
MOV DIRTY_BITS,3 ;redraw the screen and cursor
RET
TOP ENDP
;=======================================================================
; This routine moves the cursor to the bottom of the file.
;-----------------------------------------------------------------------
BOTTOM PROC NEAR
MOV DH,ROWS ;get screen size
MOV SI,LAST_CHAR ;point to last character
;-----------------------
; from TEDPLUS
DEC SI
;-----------------------
MOV LEFT_MARGIN,0 ;set window to start of line
CALL LOCATE ;adjust the cursor screen position
CALL HOME ;move cursor to start of line
MOV DIRTY_BITS,1 ;this will redraw the screen
RET
BOTTOM ENDP
;=======================================================================
; This routine moves the cursor to the start of the current line.
; If <HOME> key and already at start of line, move to start of previous line.
;-----------------------------------------------------------------------
HOME_KEY PROC NEAR
CALL LEFT ;back up one space, in case at home
HOME_KEY ENDP
HOME PROC NEAR
CALL FIND_START ;find start of line
MOV CURSOR,SI ;save the new cursor
MOV SAVE_COLUMN,0 ;save the cursor column
MOV BYTE PTR CURS_POSN,0 ;store the column number
RET
HOME ENDP
;=======================================================================
; These routines move the cursor right and left
;-----------------------------------------------------------------------
RIGHT PROC NEAR
MOV SI,CURSOR
CMP SI,LAST_CHAR ;at end of file?
JE LR_NO_CHANGE ;if yes, then can't move
;-----------------------
; from TEDPLUS
INC SI
CMP SI,LAST_CHAR ;at end of file?
DEC SI
JAE INC_RIGHT ;if yes, then increment
CMP WORD PTR [SI],CRLF ;is it CRLF?
JE MOVE_DOWN ;if yes, then move down to next line
INC_RIGHT:
;-----------------------
INC CURSOR ;advance the cursor
JMP SHORT LR_RETURN
MOVE_DOWN:
CALL HOME ;move to start of line
JMP SHORT DOWN ;and move down one row
;(CALL/RETurn)
RIGHT ENDP
;-----------------------------------------------------------------------
LEFT PROC NEAR
CMP CURSOR,0 ;at start of file?
JZ LR_NO_CHANGE ;then can't move left
MOV DX,CURS_POSN
OR DL,DL ;at first column?
JZ MOVE_UP ;if yes, then move up one
DEC CURSOR ;shift the cursor offset
LR_RETURN:
CALL CURSOR_COL ;compute column for cursor
MOV SAVE_COLUMN,DL ;save the cursor column
LR_NO_CHANGE:
MOV UNDO_LENGTH,0
RET
;-----------------------
MOVE_UP:
CALL UP ;move up to next row
JMP SHORT ENDD ;and move to end of line
;(CALL/RETurn)
LEFT ENDP
;-----------------------------------------------------------------------
; This routine moves the cursor to the end of the current line.
; If END key and already at end of line, move to end of next line.
;-----------------------------------------------------------------------
END_KEY PROC NEAR
CALL RIGHT ;move one space right, if at EOL
END_KEY ENDP
ENDD PROC NEAR
MOV SI,CURSOR
CALL FIND_EOL ;find end of this line
MOV CURSOR,SI ;store the new cursor
CALL CURSOR_COL ;compute the correct column
MOV SAVE_COLUMN,DL ;save the cursor column
RET
ENDD ENDP
;-----------------------------------------------------------------------
; This routine moves the cursor down one row. When the last row is reached,
; the screen is shifted up one row.
;-----------------------------------------------------------------------
DOWN PROC NEAR
MOV UNDO_LENGTH,0
MOV DX,CURS_POSN
CMP DH,ROWS ;at bottom row already?
MOV SI,CURSOR ;get position in file
JE SCREEN_UP ;if at bottom, then scroll up
CALL FIND_NEXT ;find the start of next line
JC DOWN_RET ;if no more lines, then return
MOV CURSOR,SI
INC DH ;advance cursor to next row
CALL SHIFT_RIGHT ;move cursor to current column
DOWN_RET:
RET
;-----------------------
SCREEN_UP:
CMP SI,LAST_CHAR ;get cursor offset
JE DOWN_RET
CALL FIND_START ;find the start of this line
MOV CURSOR,SI ;this is the new cursor
CALL FIND_NEXT ;find the offset of next line
JC SHIFT_RET ;if no more lines, then return
MOV CURSOR,SI ;this is the new cursor
MOV SI,TOP_OF_SCREEN ;get the start of the top row
CALL FIND_NEXT ;and find the next line
MOV TOP_OF_SCREEN,SI ;store the new top of screen
JMP SHORT SHIFT_RET
;(CALL/RETurn)
DOWN ENDP
;-----------------------------------------------------------------------
; This routine moves the cursor up one row. If the cursor is at the first row,
; the screen is scrolled down.
;-----------------------------------------------------------------------
UP PROC NEAR
MOV UNDO_LENGTH,0
MOV DX,CURS_POSN
MOV SI,CURSOR
OR DH,DH ;at top row already?
JZ SCREEN_DN ;if yes, then scroll down
DEC DH ;move the cursor up one row
CALL FIND_CR ;find the beginning of this row
MOV CURSOR,SI
CALL FIND_START ;find start of this row
MOV CURSOR,SI
CALL SHIFT_RIGHT ;skip over to current column
AT_TOP:
RET
;-----------------------
SCREEN_DN:
MOV SI,TOP_OF_SCREEN
OR SI,SI ;at start of file?
JZ AT_TOP ;if at top, then do nothing
CALL FIND_PREVIOUS ;find the preceding line
MOV TOP_OF_SCREEN,SI ;save new top of screen
MOV SI,CURSOR
CALL FIND_PREVIOUS ;find the preceding line
MOV CURSOR,SI ;this is the new cursor
SHIFT_RET:
OR DIRTY_BITS,1 ;need to redraw the screen
MOV SI,CURSOR
MOV DX,CURS_POSN
JMP SHIFT_RIGHT ;move cursor to current column
;(CALL/RETurn)
UP ENDP
;=======================================================================
; These four routines move the screen one page at a time by calling the
; UP and DOWN procedures.
;-----------------------------------------------------------------------
CTRL_PGDN PROC NEAR ;do PgDn then PgUp
CALL PGDN
JMP SHORT PGUP
;(CALL/RETurn)
CTRL_PGDN ENDP
;-----------------------------------------------------------------------
PGUP PROC NEAR
MOV CS:PAGE_PROC,OFFSET UP
JMP SHORT PAGE_UP_DN
PGUP ENDP
;-----------------------------------------------------------------------
CTRL_PGUP PROC NEAR ;do PgUp then PgDn
CALL PGUP
;** JMP SHORT PGDN
;(CALL/RETurn)
CTRL_PGUP ENDP
;-----------------------------------------------------------------------
PGDN PROC NEAR
MOV CS:PAGE_PROC,OFFSET DOWN
PAGE_UP_DN:
MOV CL,CS:ROWS ;get vertical length of screen
SUB CL,5 ;don't page a full screen
XOR CH,CH ;make it a word
PAGE_LOOP:
PUSH CX
CALL PAGE_PROC ;move the cursor down
POP CX
LOOP PAGE_LOOP ;loop for one page length
RET
PGDN ENDP
;=======================================================================
; This routine toggles the Insert/Overstrike mode.
;-----------------------------------------------------------------------
INSERT PROC NEAR
; ASSUME DS:NOTHING
;**RO mode NOT CS:INSERT_MODE ;toggle the switch
XOR CS:INSERT_MODE,1 ;toggle the "I/O" switch
CALL REDO_PROMPT ;redraw the insert/overstrike status
RET
INSERT ENDP
;=======================================================================
; FILE MANIPULATION services
;=======================================================================
; This routine forces the insertion of the NUL character, 00H, in the file.
;-----------------------------------------------------------------------
NUL_CHAR PROC NEAR
XOR AX,AX
NUL_CHAR ENDP
;-----------------------------------------------------------------------
; This routine adds a character into the file. In insert mode, remaining
; characters are pushed forward. If the scan code for <Enter> is detected,
; a <CR><LF> pair is added as the EOL marker. If the scan code for
; <Backspace> is detected, the character to the left of the cursor is
; deleted (unless the cursor is already at the start of the file).
;-----------------------------------------------------------------------
INSERT_KEY PROC NEAR
ASSUME DS:FILE_SEG
MOV SI,CS:CURSOR
CMP CS:INSERT_MODE,1 ;in insert mode? (0=ovr,1=ins,2 or 3=RO)
JA FILE_FULL ;jump if read-only file
CMP AH,28 ;was this the <Enter> key scan code?
JE NEW_LINE ;if yes, make new line: <CR><LF>.
CMP AH,14 ;was it the <Backspace> key scan code?
JE BACK_SPACE ;if yes, delete character left.
CMP CS:INSERT_MODE,1 ;in insert mode? (0=ovr,1=ins,2 or 3=RO)
JE INSERT_CHAR ;jump if Insert
;-----------------------
; test for overstrike character at end of line or file.
; if EOL or EOF, then insert rather than overstrike.
;-----------------------
CMP SI,CS:LAST_CHAR ;at end of file?
JAE INSERT_CHAR
INC SI
CMP SI,CS:LAST_CHAR ;at end of file?
DEC SI
JAE INSERT_CHAR
CMP WORD PTR [SI],CRLF ;at end of line?
JE INSERT_CHAR
OVERSTRIKE_NOT_AT_CRLF:
MOV DI,SI
PUSH AX ;save new character
XCHG DS:[SI],AL ;switch new character for old one
CALL SAVE_CHAR ;store the old character
POP AX ;get back new character
JMP SHORT ADVANCE
;---------------------------------------
INSERT_CHAR:
PUSH SI
PUSH AX ;save the new character
MOV AX,1
CALL OPEN_SPACE ;make room for it at SI
POP AX ;get back the new character
POP DI
JC FILE_FULL
STOSB ;insert character in file buffer, ES:DI
ADVANCE:
OR CS:DIRTY_BITS,4 ;current line is dirty
;-----------------------
; see if we made a CR-LF pair by adding a Ctrl-M with a Ctrl-J
;-----------------------
CALL C_IF_NEW_CRLF ;see if in middle of new CR-LF
JC FILE_FULL ;jump if new line was done
;------------------------
CMP AL,CR ;was key <Ctrl-M> - CR?
JNE INS_NOT_CR
MOV CS:DIRTY_BITS,1 ;redraw the screen (just in case EOL)
INS_NOT_CR:
;-----------------------
PUSH UNDO_LENGTH
CALL RIGHT ;move cursor to next letter
POP UNDO_LENGTH
FILE_FULL:
RET
CR_AT_EOF:
INC CS:CURSOR
RET
;---------------------------------------
; process the <Enter> key, make new line
;---------------------------------------
NEW_LINE:
PUSH SI
MOV AX,2
CALL OPEN_SPACE ;make space for CR and LF
POP DI ;get back old cursor location
JC FILE_FULL
MOV AX,CRLF ;LF*256+CR
STOSW ;store the CR and LF
JMP SHORT ADVANCE_NEW_LINE ;repaint the screen
;(CALL/RETurn)
INSERT_KEY ENDP
;---------------------------------------
; process the <Backspace> key, delete character left of cursor.
;---------------------------------------
BACK_SPACE PROC NEAR
OR SI,SI ;is cursor at start of file?
JZ FILE_FULL ;if so, no delete right
CALL LEFT ;move left one space (flush UnDo buffer)
;**follows JMP DEL_CHAR ;and delete the character (into buffer)
;(CALL/RETurn)
BACK_SPACE ENDP
;=======================================================================
; This routine deletes the character at the cursor by shifting the remaining
; characters forward. <Del> key
;-----------------------------------------------------------------------
DEL_CHAR PROC NEAR
CMP CS:INSERT_MODE,1 ;in insert mode? (0=ovr,1=ins,2 or 3=RO)
JA NO_DEL ;jump if read-only file
MOV CX,LAST_CHAR
MOV SI,CURSOR
MOV DI,SI
CMP SI,CX ;are we at end of file?
JAE NO_DEL ;if yes, then don't delete
LODSB ;loads char in AL, DS:SI
CALL SAVE_CHAR ;save it for UNDO function
MOV AH,[SI] ;look at the next character also
PUSH AX ;save character we're deleting
DEC LAST_CHAR ;shorten the file by one
SUB CX,SI
REP MOVSB ;move file down one, DS:SI to ES:DI
POP AX ;get back character we deleted
CMP AX,CRLF ;did we delete a CR?
JE COMBINE
OR DIRTY_BITS,4 ;current line is dirty
;-----------------------
; see if we made a CRLF by deleting a character.
; if we are in the middle of a CRLF, move the cursor back one.
;-----------------------
JMP SHORT C_IF_NEW_CRLF
;(CALL/RETurn) - saves a byte
;---------------------------------------
COMBINE: ;deleted an EOL marker, CR-LF
CALL DEL_CHAR ;now delete the line feed (recursive)
MOV DIRTY_BITS,3
MOV DX,CURS_POSN
MOV SAVE_COLUMN,DL ;save the cursor column
NO_DEL:
RET
DEL_CHAR ENDP
;=======================================================================
; see if we made a CR-LF pair with the cursor located on the LF character
; by joining the character (Ctrl-M) with a (Ctrl-J). If so, back up the
; cursor and open a new line and return with carry set. if not, return with
; carry clear.
;-----------------------------------------------------------------------
C_IF_NEW_CRLF PROC NEAR
MOV SI,CS:CURSOR ;present cursor location
OR SI,SI ;is it at start of file
JZ DONE_CRLF
CMP WORD PTR [SI-1],CRLF ;did we make CR-LF?
JNE DONE_CRLF
DEC CS:CURSOR ;move cursor back to point at CR code
C_IF_NEW_CRLF ENDP
;---------------------------------------
; This routine opens a new line.
;---------------------------------------
ADVANCE_NEW_LINE PROC NEAR ;split line on screen for CR-LF
CALL HOME ;cursor to start of line
CALL DOWN ;move down to the new line
MOV DIRTY_BITS,3 ;repaint the screen and cursor
STC ;C=1 if new line was done
RET
DONE_CRLF:
CLC ;C=0 if not new line
RET
ADVANCE_NEW_LINE ENDP
;=======================================================================
; This routine restores any characters which have recently been deleted.
;-----------------------------------------------------------------------
UNDO PROC NEAR
; ASSUME DS:FILE_SEG
XOR AX,AX
XCHG AX,CS:UNDO_LENGTH ;get buffer length
MOV SI,OFFSET UNDO_BUFFER
JMP INSERT_STRING
;(CALL/RETurn)
UNDO ENDP
;=======================================================================
; This routine inserts spaces into the file buffer. On entry, AX
; contains the number of spaces to be inserted. On return, CF=1 if
; there was not enough space in the file buffer.
;-----------------------------------------------------------------------
OPEN_SPACE PROC NEAR
; ASSUME DS:FILE_SEG
MOV CX,LAST_CHAR ;last character in the file
MOV SI,CX
MOV DI,CX
ADD DI,AX ;offset for new end of file
JC NO_ROOM ;if no more room, return error
MOV LAST_CHAR,DI ;save offset of end of file
SUB CX,CURSOR ;number of characters to shift
DEC DI
DEC SI
STD ;string moves goes forward
REP MOVSB ;shift the file upward
CLD
MOV BYTE PTR DIRTY_FILE,0FFH ;file has been altered
CLC
NO_ROOM: ;carry is set if no room in file
RET
OPEN_SPACE ENDP
;=======================================================================
; This routine adds a character to the UNDO buffer.
;-----------------------------------------------------------------------
SAVE_CHAR PROC NEAR
MOV BX,UNDO_LENGTH
OR BH,BH ;is buffer filled?
JNZ NO_SAVE ;buffer overflowed, no insert
INC UNDO_LENGTH
MOV BYTE PTR CS:UNDO_BUFFER[BX],AL
MOV BYTE PTR CS:DIRTY_FILE,0FFH ;file altered
NO_SAVE:
RET
SAVE_CHAR ENDP
;=======================================================================
; This routine deletes from the cursor position to the end of the line.
;-----------------------------------------------------------------------
DEL_EOL PROC NEAR
;-----------------------
; from TEDPLUS, modified by JEG, delete line if at column 1.
;**Shft-F8 MOV CX,CURSOR_POSN
;**Shft-F8 JCXZ DEL_L
;-----------------------
CMP CS:INSERT_MODE,1 ;in insert mode? (0=ovr,1=ins,2 or 3=RO)
JA NO_DEL_L ;jump if read-only file
MOV LINE_FLAG,0
PUSH CURSOR ;save starting cursor location
CALL ENDD ;move to the end of the line
POP SI ;get back starting cursor
MOV CX,CURSOR ;offset of end of line
MOV CURSOR,SI ;restore starting cursor
JMP SHORT DEL_END ;delete characters to end of line
;(CALL/RETurn)
DEL_EOL ENDP
;-----------------------------------------------------------------------
; This routine deletes a line, placing it in the line buffer. <F9>, <Shft-F8>
;-----------------------------------------------------------------------
DEL_L PROC NEAR
CMP CS:INSERT_MODE,1 ;in insert mode? (0=ovr,1=ins,2 or 3=RO)
JA NO_DEL_L ;jump if read-only file
MOV LINE_FLAG,1
CALL FIND_START ;find start of this line
MOV CURSOR,SI ;this will be the new cursor
PUSH SI ;save the cursor position
CALL FIND_NEXT ;find the next line
MOV CX,SI ;CX will hold line length
POP SI ;get back new cursor location
DEL_END:
MOV BYTE PTR DIRTY_FILE,0FFH ;file has been altered
SUB CX,SI ;number of bytes on line
OR CH,CH ;is line too long to fit?
JZ NOT_TOO_LONG
MOV CX,100H ;save only 256 characters
NOT_TOO_LONG:
MOV LINE_LENGTH,CX ;store length of deleted line
JCXZ NO_DEL_L
MOV DI,OFFSET LINE_BUFFER ;buffer for deleted line
PUSH CX
PUSH ES
PUSH CS
POP ES ;line buffer is in CSEG
REP MOVSB ;put deleted line in buffer
POP ES ;get back file segment
POP AX
MOV CX,LAST_CHAR ;get the file size
SUB LAST_CHAR,AX ;subtract the deleted line
MOV SI,CURSOR ;get new cursor location
MOV DI,SI
ADD SI,AX ;SI points to end of file
SUB CX,SI ;length of remaining file
JCXZ NO_DEL_L
REP MOVSB ;shift remainder of file up
NO_DEL_L:
MOV DIRTY_BITS,3 ;redraw the screen and adjust cursor
RET
DEL_L ENDP
;=======================================================================
; This routine undeletes a line by copying it from the line buffer into
; the file.
;-----------------------------------------------------------------------
UDEL_L PROC NEAR
CMP LINE_FLAG,0 ;is this an end of line only?
JE UDEL_EOL ;if yes, don't home the cursor
CALL HOME ;move cursor to home
UDEL_EOL:
MOV AX,LINE_LENGTH ;length of deleted line
MOV SI,OFFSET LINE_BUFFER
;**insert_str JMP INSERT_STRING ;(follows immediately)
;(CALL/RETurn)
UDEL_L ENDP
;-----------------------------------------------------------------------
; This routine inserts AX characters from CS:SI into the file.
; (This routine follows "UDEL_L")
;-----------------------------------------------------------------------
INSERT_STRING PROC NEAR
; ASSUME DS:FILE_SEG
PUSH SI ;save string buffer
MOV SI,CS:CURSOR ;get cursor offset
PUSH AX ;save length of string
PUSH SI
CALL OPEN_SPACE ;make space to insert string
POP DI ;get back cursor position
POP CX ;get back string length
POP SI ;get back string buffer
JC NO_SPACE ;if no space available, exit
PUSH DS
PUSH CS
POP DS
ASSUME DS:CSEG
REP MOVSB ;copy characters DS:SI to ES:DI
MOV DIRTY_BITS,3 ;redraw the screen and adjust cursor
POP DS
ASSUME DS:NOTHING
CALL C_IF_NEW_CRLF
NO_SPACE:
RET
INSERT_STRING ENDP
;=======================================================================
; VIDEO services
;=======================================================================
; This routine displays a character by writing directly to
; the screen buffer. To avoid screen noise (snow) on the color
; card (CGA), the horizontal retrace has to be monitored.
;-----------------------------------------------------------------------
WRITE_INVERSE PROC NEAR
ASSUME DS:FILE_SEG, ES:FILE_SEG
MOV BH,CS:INVERSE ;attribute for inverse video
JMP SHORT WRITE_SCREEN
;-----------------------------------------------------------------------
WRITE_FIND:
MOV BH,CS:SRCH_CLR ;attribute for find string
; MOV BH,CS:INVERSE ;attribute for find string
JMP SHORT WRITE_SCREEN
;-----------------------------------------------------------------------
WRITE_NORMAL:
MOV BH,CS:NORMAL ;attribute for normal video
WRITE_SCREEN:
MOV BL,AL ;save the character
PUSH ES
LES DX,CS:VIDEO_STATUS_REG ;video status register
;-------------------------------------------------------
; HWAIT is used to "desnow" the display for the CGA.
; If not CGA, the following instruction is modified to a
; JMP SHORT WRITE_IT
;-------------------------------------------------------
HWAIT:
IN AL,DX ;get video status
ROR AL,1 ;look at horizontal retrace
JNC HWAIT ;wait for retrace
WRITE_IT:
MOV AX,BX ;get the character/attributes
STOSW ;write the character
POP ES
RET
WRITE_INVERSE ENDP
;=======================================================================
; This routine moves the cursor to the row/column in DX.
;-----------------------------------------------------------------------
SET_CURSOR PROC NEAR
XOR BH,BH ;we're using page zero
MOV AH,2 ;BIOS set cursor function
INT 10H
RET
SET_CURSOR ENDP
;=======================================================================
; This routine computes the video buffer offset for the row/column in DX.
;-----------------------------------------------------------------------
POSITION PROC NEAR
MOV AX,CS:COLUMNS ;take columns per row
MUL DH ;times row number
XOR DH,DH
ADD AX,DX ;add the column number
SHL AX,1 ;times 2 for offset
MOV DI,AX ;return result in DI
RET
POSITION ENDP
;=======================================================================
; This routine erases from the location in DX to the right edge of the screen.
;-----------------------------------------------------------------------
ERASE_EOL PROC NEAR
ASSUME DS:NOTHING
CALL POSITION ;find screen offset
MOV CX,CS:COLUMNS ;get screen size
SUB CL,DL ;subtract current position
JCXZ NO_CLEAR
ERASE_LOOP:
MOV AL," " ;write blanks to erase
CALL WRITE_NORMAL ;display it
LOOP ERASE_LOOP
NO_CLEAR:
RET
ERASE_EOL ENDP
;=======================================================================
; This routine displays the function key prompt and insert/overstrike mode
; state. It sets DIRTY_BITS := 3 to force redraw of the entire screen
; and locate cursor.
;-----------------------------------------------------------------------
REDO_PROMPT PROC NEAR
ASSUME DS:NOTHING, ES:NOTHING
PUSH DS
PUSH CS
POP DS
ASSUME DS:CSEG
MOV DH,ROWS ;put prompt at last row
INC DH
XOR DL,DL ;and column 0
CALL POSITION ;convert to screen offset
MOV SI,OFFSET PROMPT_STRING
KEY_LOOP:
MOV AL,"F" ;display an "F"
CALL WRITE_NORMAL
LODSB
OR AL,AL ;last key in prompt?
JZ PROMPT_DONE
CALL WRITE_NORMAL
CMP BYTE PTR CS:[SI],"0" ;is it F10?
JNE TEXT_LOOP
LODSB
CALL WRITE_NORMAL
TEXT_LOOP:
LODSB
OR AL,AL ;last letter in word?
JNZ WRITE_CHAR
MOV AL," " ;display a space
CALL WRITE_NORMAL
JMP KEY_LOOP
WRITE_CHAR:
CALL WRITE_INVERSE ;display the letter
JMP TEXT_LOOP ;do the next letter
PROMPT_DONE:
MOV DH,ROWS
INC DH ;get the last row on the screen
MOV DL,PROMPT_LENGTH + 9
CALL ERASE_EOL ;erase to the end of this row
MOV BL,INSERT_MODE
XOR BH,BH
MOV AL,CS:[BX + OFFSET INSERT_CODE]
; MOV AL,"O" ;write an "O" (for "Overstrike")
; CMP INSERT_MODE,1 ;in insert mode?
; JB OVERSTRIKE
; MOV AL,"I" ;write an "I" (for "Insert")
; JE OVERSTRIKE
; MOV AL,"R" ;write an "R" (for "read-only")
OVERSTRIKE:
DEC DI ;backup one character position
DEC DI
CALL WRITE_NORMAL
MOV DIRTY_BITS,3 ;now redraw the entire screen
POP DS
ASSUME DS:NOTHING
RET
REDO_PROMPT ENDP
;=======================================================================
; This routine displays the file buffer on the screen.
; DISPLAY_BOTTOM has been deleted to reduce the program code size.
;-----------------------------------------------------------------------
DISPLAY_SCREEN PROC NEAR
ASSUME DS:FILE_SEG, ES:FILE_SEG
TEST DIRTY_BITS,2 ;see if we should adjust the cursor
JZ DISP_CURS_OK
MOV SI,CURSOR ;get the new cursor offset
MOV DX,CURS_POSN ;also get the current row
CALL LOCATE ;adjust the cursor screen position
DISP_CURS_OK:
MOV DX,CURS_POSN
MOV SAVE_ROW,DH
CALL SET_CURSOR ;position the cursor
TEST DIRTY_BITS,1 ;see if we should update the screen
JNZ DO_UPDATE_SCREEN
TEST DIRTY_BITS,4 ;is the current line dirty?
JZ DISP_DONE ;if not, take jump
CALL DISPLAY_CURRENT ;redraw the current line
JMP SHORT DISP_DONE
DO_UPDATE_SCREEN:
MOV SI,TOP_OF_SCREEN ;point to first char on screen
XOR DH,DH ;start at first row
; JMP SHORT NEXT_ROW
; ;-----------------------
;DISPLAY_BOTTOM: ;this redraws the bottom only
; CALL FIND_START ;find first character on this row
; MOV DX,CURS_POSN ;get current cursor row
NEXT_ROW:
PUSH DX
CALL DISPLAY_LINE ;display a line
POP DX
;-----------------------
; the keyboard is tested here to see if a key is waiting
;-----------------------
MOV AH,1
INT 16H
MOV AL,1 ;for dirty_bits, update not completed
JNZ DISP_NOT_DONE ;jump if there is a key is waiting
INC DH ;move to the next row
CMP DH,ROWS ;at end of screen yet?
JBE NEXT_ROW ;do all the rows
DISP_DONE:
MOV AL,0
DISP_NOT_DONE:
MOV DIRTY_BITS,AL ;screen is completely redone
RET
DISPLAY_SCREEN ENDP
;=======================================================================
; This routine displays a single line to the screen. DH holds the
; row number, SI has the offset into the file buffer. Tabs are expanded.
; Adjustment is made for side shift.
;-----------------------------------------------------------------------
DISPLAY_CURRENT PROC NEAR
; ASSUME DS:FILE_SEG
CALL FIND_START
MOV DX,CS:CURS_POSN
DISPLAY_CURRENT ENDP
;-----------------------
DISPLAY_LINE PROC NEAR ;external entry point
XOR DL,DL ;start at column zero
MOV CS:MARGIN_COUNT,DL
MOV CX,DX ;use CL to count the columns
CALL POSITION ;compute offset into video
NEXT_CHAR:
CMP SI,LAST_CHAR ;at end of file?
JAE LINE_DONE
LODSB ;get next character, DS:SI
CMP AL,TAB ;is this a Tab character?
JE EXPAND_TAB ;if yes, expand to spaces
CMP SI,LAST_CHAR ;at end of file now?
JAE DO_PUT ;jump, can't be CR-LF pair
CMP WORD PTR [SI-1],CRLF ;EOL marker
JE FOUND_CR ;quit when a CR-LF is found
DO_PUT:
CALL PUT_CHAR ;put character onto screen
TAB_DONE:
CMP CL,CS:COLUMNSB ;at right edge of screen?
JB NEXT_CHAR
;-----------------------
; from TEDPLUS
INC SI
CMP SI,LAST_CHAR ;at end of file?
DEC SI
JAE NOT_BEYOND
CMP WORD PTR [SI],CRLF ;at end of line?
;----------------------
JE NOT_BEYOND
DO_DIAMOND:
DEC DI ;backup one character
DEC DI
MOV AL,4 ;show a diamond
CALL WRITE_INVERSE ;in inverse video
NOT_BEYOND:
JMP FIND_NEXT ;find start of next line
;(CALL/RETurn)
;---------------------------------------
EXPAND_TAB:
MOV AL," " ;convert tab to spaces
CALL PUT_CHAR
MOV AL,MARGIN_COUNT
ADD AL,CL ;CL is column count
TEST AL,00000111B ;at even multiples of eight?
JNZ EXPAND_TAB ;if not, keep adding spaces
JMP TAB_DONE
;---------------------------------------
FOUND_CR:
INC SI ;past the CR-LF
LINE_DONE:
MOV DX,CX
JMP ERASE_EOL ;erase the end of the line
;(CALL/RETurn)
;-----------------------
DISPLAY_LINE ENDP
;=======================================================================
; This routine displays a single character to the screen. If the character
; is marked, it is shown in inverse video. Characters outside the current
; margin are not displayed. Characters left of the margin are skipped.
;-----------------------------------------------------------------------
PUT_CHAR PROC NEAR
; ASSUME DS:FILE_SEG
MOV BL,MARGIN_COUNT ;get distance to left margin
CMP BL,LEFT_MARGIN ;are we inside left margin?
JAE IN_WINDOW ;yes, show the character
INC BL
MOV MARGIN_COUNT,BL
RET
;-----------------------
; from TEDPLUS
IN_WINDOW:
CMP SRCH_FLG,0
JE CKM
CMP SI,SRCH_BASE
JBE CKM
CMP SI,SRCH_END
JA CKM
CALL WRITE_FIND
;**inverse CALL WRITE_INVERSE ;found string highlighted, inverse video
JMP SHORT NEXT_COL
CKM:
;-----------------------
CMP SI,MARK_START ;is this character marked?
JBE NOT_MARKED
CMP SI,MARK_END
JA NOT_MARKED
CALL WRITE_INVERSE ;marked characters shown inverse
JMP SHORT NEXT_COL
;-----------------------
NOT_MARKED:
CALL WRITE_NORMAL
NEXT_COL:
INC CL ;increment the column count
RET
PUT_CHAR ENDP
;=======================================================================
; This routine adjusts the cursor position ahead to the saved cursor column.
; On entry DH has the cursor row.
;-----------------------------------------------------------------------
SHIFT_RIGHT PROC NEAR
; ASSUME DS:FILE_SEG
MOV CL,SAVE_COLUMN ;keep the saved cursor offset
XOR CH,CH
MOV BP,CX ;keep the saved cursor position
ADD CL,LEFT_MARGIN ;shift into visible window
ADC CH,0
XOR DL,DL
MOV CURS_POSN,DX ;get cursor row/column
JCXZ NO_CHANGE
RIGHT_AGAIN:
PUSH CX
;-----------------------
; (JEG)
INC SI
CMP SI,LAST_CHAR ;at end of file?
DEC SI
JAE DO_MOVE
CMP WORD PTR [SI],CRLF ;at end of line?
;-----------------------
JE DONT_MOVE ;if at end, stop moving
DO_MOVE:
CALL RIGHT ;move right one character
DONT_MOVE:
POP CX
MOV AL,SAVE_COLUMN
XOR AH,AH
CMP AX,CX ;is cursor still in margin?
JL IN_MARGIN ;if yes, keep moving
MOV DX,CURS_POSN ;get cursor column again
XOR DH,DH
CMP DX,BP ;at saved cursor position?
JE RIGHT_DONE ;if yes, we're done
JA RIGHT_TOO_FAR ;did we go too far?
IN_MARGIN:
LOOP RIGHT_AGAIN
RIGHT_DONE:
MOV CX,BP
MOV SAVE_COLUMN,CL ;get back saved cursor position
NO_CHANGE:
RET
;-----------------------
RIGHT_TOO_FAR:
CALL LEFT ;move back left one place
MOV CX,BP
MOV SAVE_COLUMN,CL ;get back saved cursor position
RET
SHIFT_RIGHT ENDP
;=======================================================================
; This routine finds the beginning of the previous line.
;-----------------------------------------------------------------------
FIND_PREVIOUS PROC NEAR
; ASSUME DS:FILE_SEG
PUSH CURSOR ;save the cursor location
CALL FIND_CR ;find the start of this line
MOV CURSOR,SI ;save the new cursor
CALL FIND_START ;find the start of this line
POP CURSOR ;get back starting cursor
RET
FIND_PREVIOUS ENDP
;=======================================================================
; This routine searches for the previous carriage return.
; Search starts at DS:SI.
;-----------------------------------------------------------------------
FIND_CR PROC NEAR
; ASSUME DS:FILE_SEG
PUSH CX
MOV AL,LF ;look for a line feed character
MOV DI,SI
MOV CX,SI
JCXZ AT_BEGINNING
DEC DI
STD ;search backwards
LF_PREV:
REPNE SCASB ;scan for the character
;-----------------------
; from TEDPLUS
JCXZ LF_END
CMP BYTE PTR [DI],CR
JNE LF_PREV
DEC DI
LF_END:
;-----------------------
CLD ;restore the direction flag
INC DI
MOV SI,DI
AT_BEGINNING:
POP CX
RET
FIND_CR ENDP
;=======================================================================
; This routine skips past the CR and LF at SI. SI returns new offset.
;-----------------------------------------------------------------------
SKIP_CR_LF PROC NEAR
; ASSUME CS:FILE_SEG
CMP SI,LAST_CHAR ;at last char in file?
JAE NO_SKIP ;if yes, don't skip anything
CMP BYTE PTR [SI],CR ;is first character a CR?
JNE NO_SKIP
INC SI ;look at next character
CMP SI,LAST_CHAR ;is it at the end of file?
JAE NO_SKIP ;if yes, don't skip anymore
CMP BYTE PTR [SI],LF ;is next character a line feed?
JNE NO_SKIP ;skip any line feeds also
INC SI
NO_SKIP:
RET
SKIP_CR_LF ENDP
;=======================================================================
; This routine computes the location of the start of current line.
; Returns SI pointing to the first character of the current line.
;-----------------------------------------------------------------------
FIND_START PROC NEAR
; ASSUME DS:FILE_SEG
MOV SI,CURSOR ;get the current cursor
OR SI,SI ;at start of file?
JZ AT_START ;if yse, we're done
CALL FIND_CR ;find the CR
CALL SKIP_CR_LF
AT_START:
RET
FIND_START ENDP
;=======================================================================
; This routine finds the offset of the start of the next line. The search
; is started at location of ES:SI. On return, CF=1 if no CR was found.
;-----------------------------------------------------------------------
FIND_NEXT PROC NEAR
; ASSUME DS:FILE_SEG
PUSH CX
CALL FIND_EOL ;find the end of this line
JC AT_NEXT ;if at end of file, return
CALL SKIP_CR_LF ;skip past CR and LF
CLC ;indicate end of line found
AT_NEXT:
POP CX
RET
FIND_NEXT ENDP
;=======================================================================
; This routine searches for the next carriage return in the file.
; The search starts at the offset in register SI.
;-----------------------------------------------------------------------
FIND_EOL PROC NEAR
; ASSUME DS:FILE_SEG
MOV AL,CR ;look for a carriage return
CR_SCAN:
MOV CX,LAST_CHAR ;last letter in the file
SUB CX,SI ;count for the search
MOV DI,SI
JCXZ AT_END ;if nothing to search, return
REPNE SCASB ;scan for the character
MOV SI,DI ;return the location of the CR
JCXZ AT_END ;if not found, return
;-----------------------
; from TEDPLUS
CMP BYTE PTR [SI],LF
JNE CR_SCAN
;-----------------------
DEC SI ;back up one, to CR character
CLC ;indicate the CR was found
RET
AT_END:
STC ;indicate CR was not found
RET
FIND_EOL ENDP
;=======================================================================
; This routine positions the screen with the cursor at the row
; selected in register DH. On entry, SI holds the cursor offset.
;-----------------------------------------------------------------------
LOCATE PROC NEAR
; ASSUME DS:FILE_SEG
MOV CL,DH
XOR CH,CH
MOV CURSOR,SI
XOR DX,DX ;start at top of screen
OR SI,SI ;at start of buffer?
JZ LOCATE_FIRST
CALL FIND_START ;get start of this row
XOR DX,DX ;start at top of screen
OR SI,SI ;is cursor at top of file?
JZ LOCATE_FIRST
JCXZ LOCATE_FIRST ;if location to top row were done
FIND_TOP:
PUSH SI
PUSH CX
CALL FIND_CR ;find previous row
POP CX
POP AX
CMP WORD PTR [SI],CRLF
JNE LOCATE_FIRST
CMP SI,AX ;did it change?
JE LOCATE_DONE ;if not, quit moving
INC DH ;cursor moves to next row
LOOP FIND_TOP
LOCATE_DONE:
PUSH CURSOR
MOV CURSOR,SI
CALL FIND_START ;find start at top of screen
POP CURSOR
LOCATE_FIRST:
MOV TOP_OF_SCREEN,SI
MOV CURS_POSN,DX
CALL CURSOR_COL
MOV SAVE_COLUMN,DL
RET
LOCATE ENDP
;=======================================================================
; This routine computes the correct column for the cursor. No inputs.
; On exit, CURS_POSN is set and DX has row/column.
;-----------------------------------------------------------------------
CURSOR_COL PROC NEAR
; ASSUME DS:FILE_SEG
MOV SI,CURSOR ;get cursor offset
CALL FIND_START ;find start of this line
MOV CX,CURSOR ;cursor location in file
SUB CX,SI
MOV DX,CURS_POSN ;get current row
XOR DL,DL ;start at column zero
MOV MARGIN_COUNT,DL ;count past the left margin
JCXZ COL_DONE
CURSOR_LOOP:
LODSB ;get the next character
INC SI
CMP SI,LAST_CHAR
DEC SI
JAE NOT_EOL ;is <CR> without <LF>, at end of file
CMP WORD PTR [SI-1],CRLF ;(JEG)
JE COL_DONE ;if at end, we're done
NOT_EOL:
CMP AL,TAB ;is it a tab?
JNE NOT_A_TAB
MOV BL,MARGIN_COUNT
OR BL,00000111B
MOV MARGIN_COUNT,BL
CMP BL,LEFT_MARGIN ;inside visible window yet?
JB NOT_A_TAB ;if not, don't advance cursor
OR DL,00000111B ;move to multiple of eight
NOT_A_TAB:
MOV BL,MARGIN_COUNT
INC BL
MOV MARGIN_COUNT,BL
CMP BL,LEFT_MARGIN
JBE OUT_OF_WINDOW
INC DL ;we're at next column now
OUT_OF_WINDOW:
LOOP CURSOR_LOOP
COL_DONE:
CMP DL,COLUMNSB ;past end of display?
JB COLUMN_OK ;if not, we're OK
MOV DL,COLUMNSB
DEC DL ;leave cursor at last column
COLUMN_OK:
MOV CURS_POSN,DX ;store at last column
RET
CURSOR_COL ENDP
;=======================================================================
; MARK, CUT, PASTE, PRINT services
;=======================================================================
; This routine toggles the mark state and resets the paste buffer pointers.
;-----------------------------------------------------------------------
MARK PROC NEAR
XOR AX,AX
NOT CS:MARK_MODE ;toggle the mode flag
CMP CS:MARK_MODE,AL ;turning mode ON?
JNE MARK_ON
OR CS:DIRTY_BITS,1 ;need to redraw the screen
MOV CS:MARK_START,0FFFFH
JMP SHORT MARK_RET
;-----------------------
MARK_ON:
MOV AX,CS:CURSOR ;get the cursor offset
MOV CS:MARK_START,AX ;start of marked range
MARK_RET:
MOV CS:MARK_END,AX ;end of marked range
MOV CS:MARK_HOME,AX ;center of marked range
RET
MARK ENDP
;=======================================================================
; This routine removes the marked text and places it in the paste buffer.
;-----------------------------------------------------------------------
CUT PROC NEAR
CMP CS:INSERT_MODE,1 ;in insert mode? (0=ovr,1=ins,2 or 3=RO)
JA NO_MARK ;jump if read-only file
CMP CS:MARK_MODE,0 ;is the mark mode on?
JE NO_MARK ;if not, then do nothing
MOV CX,CS:MARK_END ;get end of mark region
MOV SI,CS:MARK_START ;get start of mark region
SUB CX,SI ;number of bytes selected
MOV PASTE_SIZE,CX
XOR DI,DI ;point to paste buffer
PUSH CX
PUSH ES
MOV ES,CS:PASTE_SEG ;set the paste buffer segment to ES
REP MOVSB ;deleted text to buffer, DS:SI to ES:DI
POP ES
POP AX
MOV CX,CS:LAST_CHAR
SUB CS:LAST_CHAR,AX ;shorten the file this much
MOV DI,CS:MARK_START
MOV SI,CS:MARK_END
SUB CX,SI
JCXZ NO_DELETE
REP MOVSB ;shorten the file, DS:SI to ES:DI
NO_DELETE:
MOV DX,CS:CURS_POSN
MOV SI,CS:MARK_START
CALL LOCATE ;adjust the screen position
CALL MARK ;this turns off select
;-----------------------
; see if we made a CR-LF pair by cutting the character(s) from
; between a Ctrl-M and a Ctrl-J. If so, open a new line.
;-----------------------
CALL C_IF_NEW_CRLF
NO_MARK:
RET
CUT ENDP
;=======================================================================
; This routine copies the paste buffer into the file at the cursor location.
;-----------------------------------------------------------------------
PASTE PROC NEAR
MOV AX,PASTE_SIZE ;number of characters in buffer
OR AX,AX ;any there?
JZ NO_PASTE ;if not, nothing to paste
MOV SI,CURSOR ;get cursor location
PUSH AX
PUSH SI
CALL OPEN_SPACE ;make room for new characters
POP DI
POP CX
JC NO_PASTE ;if no room, just exit
XOR SI,SI ;point to start of paste buffer
PUSH DS
MOV DS,PASTE_SEG ;segment of paste buffer
REP MOVSB ;copy in the new characters
POP DS
OR DIRTY_BITS,1 ;redraw the screen
CALL C_IF_NEW_CRLF
NO_PASTE:
RET
PASTE ENDP
;=======================================================================
; This routine prints an ASCII Form-Feed character.
;-----------------------------------------------------------------------
PRINT_FF PROC NEAR
CALL MARK ;toggle MARK "on"
PUSH DS
PUSH CS
POP DS
MOV CX,1 ;one character to print
MOV SI,OFFSET FF ;offset of last byte in paste buffer
CALL DO_FF_CHAR
POP DS
RET
FF DB 12 ;the ASCII Form-Feed character
PRINT_FF ENDP
;-----------------------------------------------------------------------
; This routine prints the marked text. If printer fails, it is cancelled.
;-----------------------------------------------------------------------
PRINT PROC NEAR
CMP MARK_MODE,0 ;is mark mode on?
JE PRINT_RET ;if not, nothing to print
MOV CX,MARK_END ;end of marked region
MOV SI,MARK_START ;start of marked region
SUB CX,SI ;number of bytes selected
JCXZ PRINT_DONE ;if nothing to print, return
DO_FF_CHAR:
XOR DX,DX ;select printer 0
MOV AH,2
INT 17H ;get printer status
TEST AH,10000000B ;is busy bit set?
JZ PRINT_DONE
TEST AH,00100000B ;is printer out of paper?
JNZ PRINT_DONE
PRINT_LOOP:
LODSB
XOR AH,AH
INT 17H ;print the character
ROR AH,1 ;check the time out bit
JC PRINT_DONE ;if set, quit printing
LOOP PRINT_LOOP
MOV AL,CR
;------ [typographical error in article]
;**nop! XOR AH,0 ;[would be No-Operation]
XOR AH,AH
;------
INT 17H ;finish with a CR
PRINT_DONE:
CALL MARK ;turn off the mark status
PRINT_RET:
RET
PRINT ENDP
;=======================================================================
; CHARACTER STRING SEARCH and SEARCH AGAIN services; HELP screen
;=======================================================================
; from TEDPLUS
; This routine is used to search or search again for a character string.
;-----------------------------------------------------------------------
FIND_STR PROC NEAR
PUSH DS
MOV BX,CS
MOV DS,BX
ASSUME DS:CSEG
;---------------------------------------
;** now ShftF6 CMP AH,66H ;Is it shift F9
CMP AH,89 ;Is it shift-F6 (JEG)
;---------------------------------------
JE RPT_FIND
MOV DH,ROWS
INC DH ;Last row on the screen
XOR DL,DL ;First column
MOV SI,OFFSET SRCH_PROMPT
CALL TTY_STRING ;Display search prompt
MOV DX, OFFSET SRCH_MAX
MOV AH,0AH
INT 21H ;Read input string
RPT_FIND:
XOR DX,DX
MOV DL,BYTE PTR SRCH_SIZ
ADD DX,OFFSET SRCH_STR
MOV DI,DX
DEC DI
MOV SRCH_END,DI
XOR DX,DX
MOV SI,CURSOR
INC SI
MOV SRCH_BASE,SI
S_REDO:
MOV DI,OFFSET SRCH_STR
MOV BX,SRCH_BASE
S_CYCLE: MOV AL,[DI]
MOV AH,AL ;CONVERT AL TO OPPOSITE AND PUT IN AH
CMP AL,'A'
JB S_CMP
CMP AL,'Z'
JA TSTLO
XOR AH,20H ;toggle letter case
JMP SHORT S_CMP
TSTLO: CMP AL,'a'
JB S_CMP
CMP AL,'z'
JA S_CMP
XOR AH,20H ;toggle letter case
S_CMP: CMP BX,LAST_CHAR
JA END_MCH
CMP AL,ES:[BX]
JE S_MCH
CMP AH,ES:[BX]
JE S_MCH
CMP DI,OFFSET SRCH_STR
JNE S_REDO
INC BX
CMP WORD PTR ES:[BX-1],CRLF ;test for EOL marker
JNE S_BX1
INC DL
S_BX1: JMP SHORT S_CMP
S_MCH: INC BX
CMP DI,OFFSET SRCH_STR
JNE NO_BSE
MOV SRCH_BASE,BX
NO_BSE: ADD DH,DL
XOR DL,DL
CMP DI,SRCH_END
JE YEA_MCH
INC DI
JMP SHORT S_CYCLE
YEA_MCH:
MOV SRCH_FLG,1
MOV SI,SRCH_BASE
DEC SI
MOV SRCH_BASE,SI
MOV CURSOR,SI
XOR BX,BX
MOV BL,BYTE PTR SRCH_SIZ
ADD BX,SI
MOV SRCH_END,BX
XOR DL,DL
ADD DX,CURS_POSN
CMP DH,ROWS
JBE NEW_S
XOR DX,DX
NEW_S:
POP DS ;ASSUME is no longer valid
CALL LOCATE
JMP SHORT END_MCH2
END_MCH:
POP DS
END_MCH2:
CALL REDO_PROMPT ;redraw the prompt and the screen
RET
FIND_STR ENDP
;=======================================================================
; This routine displays a user help screen
;-----------------------------------------------------------------------
HELP PROC NEAR
PUSH DS
PUSH CS
POP DS
ASSUME DS:CSEG ;for func-9, display message
CALL CLR_SCREEN
MOV DX,OFFSET HELP_SCREEN ;message is at DS:DX in CSEG
MOV AH,9 ;display message
INT 21H
HELP_WAIT_KEY:
MOV AH,1 ;wait for any key pressed
INT 16H
JZ HELP_WAIT_KEY
MOV AH,0
INT 16H
POP DS
ASSUME DS:NOTHING
JMP REDO_PROMPT ;redraw the prompt and the screen
;(CALL/RETurn)
HELP ENDP
;=======================================================================
; This routine prompts for a filename then writes the file. The original
; file is renamed to filename.BAK. If an invalid filename is entered, the
; speaker is beeped. If the file has not been altered, the edit file is
; abandoned and control is immediately returned to DOS.
;-----------------------------------------------------------------------
EXIT PROC NEAR
PUSH DS ;save in case ESC key
PUSH ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
ASSUME DS:CSEG, ES:CSEG
TEST BYTE PTR DIRTY_FILE,0FFH ;is the file altered?
JNZ NEXT_LETTER ;(jump was out of range)
JMP FINISHED ;file has not been altered, no save.
;-----------------------
IS_BACKSPACE:
CMP DI,NAME_POINTER ;at first letter?
JLE NEXT_LETTER ;if yes, don't erase it
MOV BYTE PTR [DI-1],0
DEC NAME_END
NEXT_LETTER:
MOV DH,ROWS
INC DH ;last row on the screen
XOR DL,DL ;first column
MOV SI,OFFSET SAVE_MESS
PUSH DX
CALL TTY_STRING ;display a prompt
POP DX
ADD DL,9 ;move right 9 spaces
MOV SI,NAME_POINTER
CALL TTY_STRING ;display the filename
XOR AH,AH ;read the next key
INT 16H
MOV DI,NAME_END ;this points to the last letter
OR AL,AL ;is it a real character?
; JZ NEXT_LETTER ;ignore special keys
JZ IS_ESCAPE ;special keys return to edit mode
CMP AL,1BH ;is it ESCape?
JE IS_ESCAPE ;continue with exit procedure
CMP AL,CR ;is it CR?
JE GOT_NAME
CMP AL,08H ;is it a backspace?
JE IS_BACKSPACE
CMP DI,81H + 65 ;too many letters?
JG NEXT_LETTER ;if yes, ignore them
XOR AH,AH ;for new terminal 0 to string
STOSW ;store the new letter and 0
INC NAME_END ;name is one character longer
JMP NEXT_LETTER ;read another keystroke
;-----------------------
IS_ESCAPE:
POP ES ;get back file segment
POP DS
JMP REDO_PROMPT ;redraw the prompt and the screen
;(CALL/RETurn) ;return to edit mode
;-----------------------
GOT_NAME:
MOV DX,NAME_POINTER ;point to the filename
MOV AX,4300H ;get the file attribute
INT 21H ;DOS call
JNC NAME_OK ;if no error, filename is OK
CMP AX,3 ;was it "path not found" error?
JE BAD_NAME ;if yes, filename was bad
NAME_OK:
MOV SI,OFFSET DOT_$$$ ;point to the ".$$$"
MOV DI,OFFSET NAME_DOT_$$$
CALL CHG_EXTENSION ;add the new extension
MOV DX,OFFSET NAME_DOT_$$$ ;point to temp filename
MOV CX,0020H ;attribute for new file
MOV AH,3CH ;function to create file
INT 21H ;DOS call
JNC NAME_WAS_OK ;continue if name was OK
BAD_NAME:
MOV AX,0E07H ;write a bell character
INT 10H ;BIOS TTY service
JMP NEXT_LETTER ;get another letter
;-----------------------
WRITE_ERROR:
MOV AH,3EH ;close the file
INT 21H ;DOS call
JMP BAD_NAME ;filename must be bad
;-----------------------
NAME_WAS_OK:
XOR DX,DX ;this is the file buffer
MOV CX,LAST_CHAR ;number of chars in file
MOV BX,AX ;this is the handle
MOV AH,40H ;write to the file
POP DS ;recover the buffer segment
INT 21H ;write the buffer contents
POP DS
JC WRITE_ERROR ;exit on a write error
CMP AX,CX ;was entire file written?
JNE WRITE_ERROR ;if not, exit
PUSH CS
POP DS ;get the code segment
MOV AH,3EH ;close the temp file
INT 21H ;DOS call
MOV SI,OFFSET DOT_BAK ;point to the ".BAK"
MOV DI,OFFSET NAME_DOT_BAK
CALL CHG_EXTENSION ;make the backup filename
MOV DX,OFFSET NAME_DOT_BAK ;point to the backup name
MOV AH,41H ;delete existing backup file
INT 21H ;DOS call
MOV DI,OFFSET NAME_DOT_BAK
MOV DX,NAME_POINTER
MOV AH,56H
INT 21H ;DOS call
MOV DI,NAME_POINTER ;point to new filename
MOV DX,OFFSET NAME_DOT_$$$ ;point to temporary file
MOV AH,56H ;rename temp to new file
INT 21H ;DOS call, rename
POP AX ;restore the stack
POP AX
JMP SHORT FINISHED
;(To return to DOS)
EXIT ENDP
;-----------------------------------------------------------------------
; This routine prompts for a verify keystroke then exits without saving
; the file. If the file has not been altered, the edit file is abandoned
; and control is immediately returned to DOS.
;-----------------------------------------------------------------------
ABORT PROC NEAR
PUSH CS
POP DS
ASSUME DS:CSEG
TEST BYTE PTR DIRTY_FILE,0FFH ;is the file altered?
JZ FINISHED ;file has not been altered.
MOV DH,ROWS ;last row on display
INC DH ;bottom row on screen
XOR DL,DL ;first column
MOV SI,OFFSET VERIFY_MESS
CALL TTY_STRING ;display verify message
XOR AH,AH ;read the next key
INT 16H ;BIOS read key routine
CMP AL,CR ;is it CR?
JE FINISHED
OR AL,20H ;convert to lower case
CMP AL,"y" ;was answer Yes?
JE FINISHED ;if yes, then we're finished
CALL REDO_PROMPT ;redraw the prompt and the screen
PUSH ES
POP DS ;set DS back to file segment
RET ;return to edit mode
;-----------------------
FINISHED:
MOV DX,OFFSET COPYRIGHT
;-----------------------
EXIT_TO_DOS: ;External entry point (DX, message)
PUSH CS
POP DS ;point to code segment
ASSUME DS:CSEG
CALL CLR_SCREEN ;clear the screen (passes DX through)
MOV AH,9 ;display error/exit message DS:DX
INT 21H ;DOS call
MOV AX,EXIT_CODE ;Exit to DOS with exit code
INT 21H ;DOS call
ABORT ENDP
;=======================================================================
; This routine copies the input filename to CS:DI and changes the extension.
;-----------------------------------------------------------------------
CHG_EXTENSION PROC NEAR
ASSUME DS:CSEG, ES:CSEG
PUSH SI
MOV SI,NAME_POINTER
CHG_LOOP:
LODSB
CMP AL,"." ;look for the extension
JE FOUND_DOT
OR AL,AL
JZ FOUND_DOT
STOSB ;copy a character
JMP CHG_LOOP
;-----------------------
FOUND_DOT:
MOV CX,5 ;five characters in extension
POP SI
REP MOVSB ;move new extension in
RET
CHG_EXTENSION ENDP
;=======================================================================
; This routine displays the string at CS:SI at the location in DX. The
; remainder of the row is erased. Cursor is put at the end of the line.
;-----------------------------------------------------------------------
TTY_STRING PROC NEAR
ASSUME DS:CSEG
PUSH DX
CALL POSITION ;compute offset into video
POP DX
TTY_LOOP:
LODSB ;load DS:SI to AL
OR AL,AL ;at end of string yet?
JZ TTY_DONE
INC DL
PUSH DX
CALL WRITE_INVERSE ;write in inverse video
POP DX
JMP TTY_LOOP
;-----------------------
TTY_DONE:
CALL SET_CURSOR ;move cursor to end of string
JMP ERASE_EOL ;erase the rest of the line
;(CALL/RETurn)
TTY_STRING ENDP
;=======================================================================
; This routine clears the screen, leaves registers unaltered.
;-----------------------------------------------------------------------
CLR_SCREEN PROC NEAR
ASSUME DS:NOTHING
PUSH AX ;save registers (for INT-24)
PUSH BX
PUSH CX
PUSH DX ;save DX for message address
MOV DL,79 ;DL = right column
MOV DH,CS:ROWS ;DH = lower row
INC DH ;to clear the last line on the screen
MOV CX,0 ;CH = upper row, CL = left column
MOV BH,CS:NORMAL ;BH is blank line attribute, 07 = normal video
MOV AX,0600H ;scroll the cursor up (blank line)
INT 10H
XOR DX,DX ;set cursor to top left of screen
CALL SET_CURSOR
POP DX
POP CX
POP BX
POP AX
RET
CLR_SCREEN ENDP
;=======================================================================
; This is the control break handler for MS-DOS. It ignores the break.
; TED then responds by entering a <Ctrl-C> in the file.
;-----------------------------------------------------------------------
NEWINT23 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
MOV CS:DIRTY_BITS,1 ;to redraw the screen
CLC ;tell DOS to ignore break
IRET
NEWINT23 ENDP
;=======================================================================
; This is the severe error handler. It homes the cursor before
; processing the error.
;-----------------------------------------------------------------------
NEWINT24 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
PUSHF
CALL CLR_SCREEN ;clear the screen and home the cursor
POPF
JMP CS:OLDINT24
;(chain to old INT 24 handler)
NEWINT24 ENDP
;=======================================================================
; This is the location of the character string search buffer. It occupies
; the last three bytes in the program file. The following 64-bytes are used
; for the buffer area. The third byte is also used as the program code
; check-sum compensation byte.
;-----------------------------------------------------------------------
EVEN
SRCH_MAX DB 66 ;42H, maximum size of buffer
SRCH_SIZ DB 0 ;number of char's actually in buffer
SRCH_STR DB 0 ;start of buffer block
;This is the correction byte for the check-sum test
CHEK_SUM_BYT DB 0103
;=======================================================================
NAME_DOT_$$$ EQU $ +64 ;128 bytes
NAME_DOT_BAK EQU $+80H +64 ;128 bytes
UNDO_BUFFER EQU $+100H +64 ;256 bytes, Del key buffer
LINE_BUFFER EQU $+200H +64 ;256 bytes, Delete line, EOL
END_BUFFER EQU $+300H +64 +15 ;end of buffers, file segment follows
; +15 is for paragraph rounding
CSEG ENDS
;-----------------------------------------------------------------------
FILE_SEG SEGMENT
FILE_SEG ENDS
END START